﻿namespace DomainServices.Tools
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.ServiceModel.DomainServices;
    using System.ServiceModel.DomainServices.Hosting;
    using System.ServiceModel.DomainServices.Server;

    internal class DomainServiceCatalog
    {
        private HashSet<string> _assembliesToLoad;
        private List<DomainServiceDescription> _domainServiceDescriptions;
        private Dictionary<Assembly, bool> _loadedAssemblies;
        private ILogger _logger;

        public DomainServiceCatalog(IEnumerable<string> assembliesToLoad, ILogger logger)
        {
            this._domainServiceDescriptions = new List<DomainServiceDescription>();
            if (assembliesToLoad == null)
            {
                throw new ArgumentNullException("assembliesToLoad");
            }
            if (logger == null)
            {
                throw new ArgumentNullException("logger");
            }
            this._logger = logger;
            this._assembliesToLoad = new HashSet<string>(assembliesToLoad, StringComparer.OrdinalIgnoreCase);
            this.LoadAllAssembliesAndSetAssemblyResolver();
            this.AddDomainServiceDescriptions();
            this.ValidateDomainServiceInheritance();
        }

        public DomainServiceCatalog(IEnumerable<Type> domainServiceTypes, ILogger logger)
        {
            this._domainServiceDescriptions = new List<DomainServiceDescription>();
            if (domainServiceTypes == null)
            {
                throw new ArgumentNullException("domainServiceTypes");
            }
            if (logger == null)
            {
                throw new ArgumentNullException("logger");
            }
            this._logger = logger;
            foreach (Type type in domainServiceTypes)
            {
                this.AddDomainServiceType(type);
            }
            this.ValidateDomainServiceInheritance();
        }

        public DomainServiceCatalog(Type domainServiceType, ILogger logger)
        {
            this._domainServiceDescriptions = new List<DomainServiceDescription>();
            if (domainServiceType == null)
            {
                throw new ArgumentNullException("domainServiceType");
            }
            if (logger == null)
            {
                throw new ArgumentNullException("logger");
            }
            this._logger = logger;
            this.AddDomainServiceType(domainServiceType);
        }

        private void AddDomainServiceDescriptions()
        {
            foreach (KeyValuePair<Assembly, bool> pair in this._loadedAssemblies)
            {
                if (pair.Value)
                {
                    foreach (Type type in AssemblyUtilities.GetExportedTypes(pair.Key, this._logger))
                    {
                        if (typeof(DomainService).IsAssignableFrom(type) && (TypeDescriptor.GetAttributes(type)[typeof(EnableClientAccessAttribute)] != null))
                        {
                            this.AddDomainServiceType(type);
                        }
                    }
                    continue;
                }
            }
        }

        private void AddDomainServiceType(Type domainServiceType)
        {
            if (TypeDescriptor.GetAttributes(domainServiceType)[typeof(EnableClientAccessAttribute)] == null)
            {
                this.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_InvalidDomainServiceType, new object[] { domainServiceType.Name }));
            }
            else
            {
                Type dbContextType = DbContextUtilities.GetDbContextType(domainServiceType);
                if (dbContextType != null)
                {
                    Type dbContextTypeReference = DbContextUtilities.GetDbContextTypeReference(dbContextType);
                    DbContextUtilities.SetDbInitializer(dbContextType, dbContextTypeReference, null);
                }
                try
                {
                    DomainServiceDescription providerDescription = this.GetProviderDescription(domainServiceType);
                    if (providerDescription != null)
                    {
                        if (!providerDescription.EntityTypes.Any<Type>() && !Enumerable.Any<DomainOperationEntry>(providerDescription.DomainOperationEntries, p => p.Operation == DomainOperation.Invoke))
                        {
                            goto Label_00B8;
                        }
                        this._domainServiceDescriptions.Add(providerDescription);
                    }
                    return;
                Label_00B8:;
                    this.LogWarning(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_EmptyDomainService, new object[] { domainServiceType.Name }));
                }
                catch (ArgumentException exception)
                {
                    this.LogError(exception.Message);
                }
                catch (InvalidOperationException exception2)
                {
                    this.LogError(exception2.Message);
                }
            }
        }

        private DomainServiceDescription GetProviderDescription(Type providerType)
        {
            try
            {
                return DomainServiceDescription.GetDescription(providerType);
            }
            catch (Exception exception)
            {
                if (exception.IsFatal())
                {
                    throw;
                }
                this.LogError(exception.Message);
            }
            return null;
        }

        private void LoadAllAssembliesAndSetAssemblyResolver()
        {
            this._loadedAssemblies = new Dictionary<Assembly, bool>();
            foreach (string str in this._assembliesToLoad)
            {
                Assembly assembly = AssemblyUtilities.LoadAssembly(str, this._logger);
                if (assembly != null)
                {
                    this._loadedAssemblies[assembly] = !assembly.IsSystemAssembly();
                }
            }
            AssemblyUtilities.SetAssemblyResolver(this._loadedAssemblies.Keys);
        }

        private void LogError(string message)
        {
            if (this._logger != null)
            {
                this._logger.LogError(message);
            }
        }

        private void LogWarning(string message)
        {
            if (this._logger != null)
            {
                this._logger.LogWarning(message);
            }
        }

        private void ValidateDomainServiceInheritance()
        {
            HashSet<Type> set = new HashSet<Type>(from d in this.DomainServiceDescriptions select d.DomainServiceType);
        Label_00AC:
            foreach (Type type in set)
            {
                for (Type type2 = type.BaseType; type2 != typeof(DomainService); type2 = type2.BaseType)
                {
                    if (TypeDescriptor.GetAttributes(type)[typeof(EnableClientAccessAttribute)] == null)
                    {
                        goto Label_00AC;
                    }
                    if (set.Contains(type2))
                    {
                        this.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_DomainService_Inheritance_Not_Allowed, new object[] { type, type2 }));
                        goto Label_00AC;
                    }
                }
            }
        }

        public ICollection<DomainServiceDescription> DomainServiceDescriptions
        {
            get
            {
                return this._domainServiceDescriptions;
            }
        }
    }
}

